#pragma once
#include <vector>
#include <string>

class ByteBuffer
{
	typedef std::vector< unsigned char >	BufferContainer;
public:
	ByteBuffer( unsigned int size = 256 );
	~ByteBuffer();

	void Reset();

	char* Data();
	const char* Data() const;
	char* WriteData();

	unsigned int Size() const;
	unsigned int PacketSize() const;

	void ShiftRead( unsigned int offset );
	void ShiftWrite( unsigned int offset );

	template < typename NT >	// NumericType
	bool Pop( NT &val );

	bool Pop( std::string &val, unsigned int len );
	bool Pop( char* val, unsigned int len );

	template < typename NT >	// NumericType
	void Push( NT val );

	void Push( const std::string &val );
	void Push( const char* val, unsigned int len );

	void Shift( unsigned int size );
	void Reserve( unsigned int size );

private:
	ByteBuffer( const ByteBuffer& );
	ByteBuffer& operator =( const ByteBuffer& );

	void Resize( unsigned int size );

private:
	BufferContainer	m_buffer;
	unsigned int			m_size;
	unsigned int			m_read;
	unsigned int			m_write;
};

inline void ByteBuffer::Reset()
{
	m_read = 0;
	m_write = 0;
}

inline char* ByteBuffer::Data()
{
	return reinterpret_cast<char*>( &m_buffer[m_read] );
}

inline const char* ByteBuffer::Data() const
{
	return reinterpret_cast< const char* >( &m_buffer[m_read] );
}

inline unsigned int ByteBuffer::Size() const
{
	return m_write - m_read; 
}

inline void ByteBuffer::ShiftRead( unsigned int offset )
{
	m_read += offset;
	if( m_read == m_write )
		m_write = m_read = 0;
}

inline char* ByteBuffer::WriteData()
{
	return reinterpret_cast< char* >( &m_buffer[m_write] );
}

inline void ByteBuffer::ShiftWrite( unsigned int offset )
{
	m_write += offset;
}

template < typename NT >	// NumericType
inline bool ByteBuffer::Pop( NT &val )
{
	return Pop( reinterpret_cast< char* >( &val ), sizeof( val ) );
}

inline bool ByteBuffer::Pop( std::string &val, unsigned int len )
{
	if( len <= Size() )
	{
		val.append( Data(), len );
		m_read += len;
		return true;
	}

	return false;
}

inline bool ByteBuffer::Pop( char* val, unsigned int len )
{
	if( len < Size() )
	{
		memcpy( val, Data(), len );
		m_read += len;
		return true;
	}

	return false;
}

template < typename NT >	// NumericType
inline void ByteBuffer::Push( NT val )
{
	Push( reinterpret_cast< const char* >( &val ), sizeof( val ) );
}

inline void ByteBuffer::Push( const std::string &val )
{
	Push( val.c_str(), static_cast< unsigned int >( val.size() ) );
}

inline void ByteBuffer::Reserve( unsigned int size )
{
	if( m_size - m_write < size )
		Resize( Size() + size );
}

inline void ByteBuffer::Push( const char* val, unsigned int len )
{
	Reserve( len );

	memcpy( WriteData(), val, len );
	m_write += len;
}

inline unsigned int ByteBuffer::PacketSize() const
{
	return *reinterpret_cast< const unsigned int* >( Data() );
}

